//============================================================================
//                                  I B E X                                   
// File        : ibex_ExprSimplify2.h
// Author      : Gilles Chabert
// Copyright   : IMT Atlantique (France)
// License     : See the LICENSE file
// Created     : Mar 27, 2020
// Last update : May 15, 2020
//============================================================================

#ifndef __IBEX_EXPR_SIMPLIFY_2_H__
#define __IBEX_EXPR_SIMPLIFY_2_H__

#include "ibex_ExprVisitor.h"
#include "ibex_NodeMap.h"
#include "ibex_ExprPolynomial.h"
#include "ibex_IntervalMatrix.h"
#include "ibex_Expr2Polynom.h"

namespace ibex {

/**
 * \ingroup symbolic
 *
 */
class ExprSimplify2 : public virtual ExprVisitor<const ExprNode*> {
public:

	ExprSimplify2(bool develop=false);

	/**
	 * \warning The function destroys all unused nodes which
	 * may include "e" itself (so the node "e" may not exist
	 * after calling this function).
	 * However, it does not delete symbols.
	 */
	const ExprNode& simplify(const ExprNode& e);

	/*
	 * If some nodes should not be removed although they
	 * do not belong to the simplified expression, add
	 * them in this map. (note: this is a kind of hack,
	 * a better solution could be found).
	 */
	NodeMap<bool> lock;

protected:
	friend class ExprMonomial;
	friend class Expr2Polynom;

	const ExprNode* visit(const ExprNode& e);
	const ExprNode* visit(const ExprIndex& i);
	const ExprNode* visit(const ExprSymbol& x);
	const ExprNode* visit(const ExprConstant& c);
	const ExprNode* visit(const ExprVector& e);
	const ExprNode* visit(const ExprApply& e);
	const ExprNode* visit(const ExprChi& e);
	const ExprNode* visit(const ExprGenericBinaryOp& e);
	const ExprNode* visit(const ExprAdd& e);
	const ExprNode* visit(const ExprMul& e);
	const ExprNode* visit(const ExprSub& e);
	const ExprNode* visit(const ExprDiv& e);
	const ExprNode* visit(const ExprMax& e);
	const ExprNode* visit(const ExprMin& e);
	const ExprNode* visit(const ExprAtan2& e);
	const ExprNode* visit(const ExprGenericUnaryOp& e);
	const ExprNode* visit(const ExprMinus& e);
	const ExprNode* visit(const ExprTrans& e);
	const ExprNode* visit(const ExprSign& e);
	const ExprNode* visit(const ExprAbs& e);
	const ExprNode* visit(const ExprPower& e);
	const ExprNode* visit(const ExprSqr& e);
	const ExprNode* visit(const ExprSqrt& e);
	const ExprNode* visit(const ExprExp& e);
	const ExprNode* visit(const ExprLog& e);
	const ExprNode* visit(const ExprCos& e);
	const ExprNode* visit(const ExprSin& e);
	const ExprNode* visit(const ExprTan& e);
	const ExprNode* visit(const ExprCosh& e);
	const ExprNode* visit(const ExprSinh& e);
	const ExprNode* visit(const ExprTanh& e);
	const ExprNode* visit(const ExprAcos& e);
	const ExprNode* visit(const ExprAsin& e);
	const ExprNode* visit(const ExprAtan& e);
	const ExprNode* visit(const ExprAcosh& e);
	const ExprNode* visit(const ExprAsinh& e);
	const ExprNode* visit(const ExprAtanh& e);
	const ExprNode* visit(const ExprFloor& e);
	const ExprNode* visit(const ExprCeil& e);
	const ExprNode* visit(const ExprSaw& e);

	const ExprNode* nary(const ExprNAryOp& e,
			std::function<Domain(Array<const Domain>&)> fcst,
			std::function<const ExprNAryOp&(Array<const ExprNode>&)> f);

	const ExprNode* unary(const ExprUnaryOp& e,
			std::function<Domain(const Domain&)> fcst,
			std::function<const ExprNode&(const ExprNode&)> f);

	const ExprNode* binary(const ExprBinaryOp& e,
			std::function<Domain(const Domain&, const Domain&)> fcst,
			std::function<const ExprBinaryOp&(const ExprNode&, const ExprNode&)> f);

	/*
	 * Record the node in #record for cleanup
	 */
	const ExprNode& rec(const ExprNode&);

	/**
	 * Polynomial generator (performs simplification)
	 */
	Expr2Polynom _2polynom;

	/*
	 * All nodes generated by the simplification process.
	 * (for cleanup)
	 */
	std::vector<const ExprNode*> record;

};

} // namespace ibex

#endif // __IBEX_EXPR_SIMPLIFY_2_H__
